package pay.serviceImpl;

import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.http.client.utils.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import pay.common.ConstantFYJZH;
import pay.common.ConstantOrderStatus;
import pay.common.ConstantsOrder;
import pay.entity.*;
import pay.portal.web.message.PreAuthMsg;
import pay.portal.web.message.PreAuthResultMsg;
import pay.service.*;
import pay.service.sys.ISysUser;
import pay.utils.*;

@Service
public class PreAuthImpl implements IPreAuth {

    protected Logger logger = LoggerFactory
            .getLogger(this.getClass().getName());

    /**
     * 商户代码，从配置文件中读取
     */

    @Value("${fy.mchntCd}")
    private String mchnt_cd;

    @Value("${server.scheme}")
    private String serverScheme;

    @Value("${fy.goldAccount.pathBaseUrl}")
    private String pathBaseUrl; // 测试环境为/jzh 生产无。

    @Value("${server.url}")
    private String serverUrl;

    @Value("${fy.withdrawLogFilePath}")
    private String logFilePath;

    /**
     * 金账户baseURl，从配置文件中读取
     */
    @Value("${fy.goldAccount.baseUrl}")
    private String jzhBaseUrl;

    @Autowired
    private ISysUser sysUserService;


    @Autowired
    private IApiSecret apiSecretService;

    @Autowired
    private IProduct prdService;

    @Autowired
    private ITrade tradeService;

    @Autowired
    private IOrderHelper orderHelperService;

    @Override
    public PreAuthResultMsg PreAuth(PreAuthMsg req) {

        PreAuthResultMsg resultMsg = new PreAuthResultMsg();
        resultMsg.setAmtBigDecimal(null);
        resultMsg.setContract_no(null);
        resultMsg.setMchnt_txn_ssn(null);
        resultMsg.setRespMsg("");
        resultMsg.setResult(false);


        Boolean result = false;


        logger.info("进入8082 富友预授权接口");


        Long userId = req.getUserId();

        // 产品id
        Long productId = null;

        // 出账账户
        String out_cust_no = "";

        // 入账账户
        String in_cust_no = "";

        // 订单id
        Long orderId = req.getOrderId();


        // 流水号
        String mchnt_txn_ssn = getSSN(orderId);

        // 金额
        BigDecimal remBigDecimal = null;

        String rem = null;

        OrderInfo order = null;

        String token = req.getToken();

        String sign = req.getSign();

        String apiKey = req.getApiKey();


        logger.info(
                "PreAuthCtrl 入参 orderId ={}, userId={}, token={}, sign={}, apiKey={}",
                orderId, userId, token, sign, apiKey);


        if (orderId == null) {
            logger.info("redis 中订单id 为空 orderId = {}", orderId);
            resultMsg.setRespMsg("预授权订单不存在！");
            resultMsg.setResult(false);
            return resultMsg;
        }

        order = orderHelperService
                .getFromRedis(ConstantsOrder.ORDER_FYJZH_PRIX + orderId);
        if (order == null) {
            logger.info("进入富友预授权接口， redis 中订单数据为空 orderId = {}", orderId);
            resultMsg.setRespMsg("预授权订单不存在！");
            resultMsg.setResult(false);

            return resultMsg;
        }

        // 01 . 检查传入参数是否全部不为空
        if (StringHelper.isEmpty(token) || StringHelper.isEmpty(sign)
                || StringHelper.isEmpty(apiKey) || userId == null) {

            logger.info("进入富友预授权接口，部分参数为空，预授权失败。");

            resultMsg.setRespMsg("预授权参数错误！");
            resultMsg.setResult(false);
            return resultMsg;
        }

        productId = order.getProductId();
        ProductInfo productInfo = prdService.findById(productId);

        SysUser user = sysUserService.findById(order.getUserId());

        if (productInfo == null || user == null) {
            logger.info("进入富友预授权接口，数据错误， productInfo 或 user为空");
            resultMsg.setRespMsg("预授权数据错误！");
            resultMsg.setResult(false);
            return resultMsg;
        }

        // 02.判断token 是否存在
        if (!checkToken(token, userId)) {
            logger.info("进入富友预授权接口 token:{}  is not exits", token);
            resultMsg.setRespMsg("预授权数据错误， token 不存在！");
            resultMsg.setResult(false);
            return resultMsg;
        }

        ApiSecret apiSecret = apiSecretService.findByApikey(apiKey);

        String scretStr = "";

        if (apiSecret != null) {
            scretStr = apiSecret.getApiSecret();
        }

        // 03 .验证签名是否正确
        Map<String, String> map = getRequestMap(orderId, userId, token, sign,
                apiKey);

        boolean checkResult = EncryptDecryptUtils.validateSignByMap(scretStr,
                map);

        if (!checkResult) {
            logger.info("进入富友预授权接口，验证失败");

            resultMsg.setRespMsg("进入富友预授权接口，验证失败！");
            resultMsg.setResult(false);

            return resultMsg;
        }

        out_cust_no = user.getName();
        in_cust_no = order.getCreditorPhoneNum();
        remBigDecimal = order.getSuccessAmount();

        rem = BalanceUtils.chgToFYVal(remBigDecimal);

        logger.info(
                "进入富友预授权接口关键入参mchnt_txn_ssn={},out_cust_no={},in_cust_no={},rem={}",
                mchnt_txn_ssn, out_cust_no, in_cust_no, rem);

        if (StringHelper.isEmpty(mchnt_txn_ssn)
                || StringHelper.isEmpty(out_cust_no)
                || StringHelper.isEmpty(in_cust_no)
                || StringHelper.isEmpty(rem)) {
            logger.info("进入富友预授权接口，参数检查， 入参有空参数");
            resultMsg.setRespMsg("进入富友预授权接口，参数检查， 入参有空参数！");
            resultMsg.setResult(false);

            return resultMsg;
        }


        // 调用富友接口，进行冻结
        RedisCilent.setString(mchnt_txn_ssn, orderId.toString(), 172800);

        String resultStr = sendPostToFY(mchnt_txn_ssn, out_cust_no, in_cust_no,
                rem);

        String resp_code = XmlUtils.getVal(resultStr, "resp_code");

        String contract_no = XmlUtils.getVal(resultStr, "contract_no");

        logger.info("进入富友预授权接口， resultStr ={}, resp_code={}, contract_no={}", resultStr, resp_code, contract_no);


        String resp_desc = "";

        if (resp_code.equals("0000")) {
            logger.info("扣款成功 orderId={}, resp_code={}", orderId, resp_code);
            resp_desc = "预授权成功";
            result = true;
        } else if (resultStr.equals(ConstantFYJZH.HTTP_ERROR_RETURN)
                || resp_code.equals("5138")) {
            logger.info("获取请求结果异常。orderId={}, resp_code={}", orderId, resp_code);
            resp_desc = "失败";
            result = false;
        } else if (resp_code.equals("3018")) {
            logger.info("账户可用余额不足。orderId={}, resp_code={}", orderId, resp_code);
            resp_desc = "余额不足";
            result = false;
        } else {
            logger.info("预授权扣款失败，请重试。 orderId={}", orderId);
            resp_desc = "失败";
            result = false;
        }


        //添加到交易记录表中
        addIntoTradeTable(orderId, userId, out_cust_no, remBigDecimal, mchnt_txn_ssn, contract_no, resp_code, resp_desc);

        //写入日志文件中
        addIntoLogFile(out_cust_no, in_cust_no, mchnt_txn_ssn, rem, resp_code, resp_desc);

        resultMsg.setAmtBigDecimal(remBigDecimal);
        resultMsg.setRespMsg(resp_desc);
        resultMsg.setResult(result);
        resultMsg.setMchnt_txn_ssn(mchnt_txn_ssn);
        resultMsg.setContract_no(contract_no);
        resultMsg.setOrderId(orderId);
        return resultMsg;

    }


    @Override
    public void updPreautResultTask( String mchnt_txn_ssn, String contract_no, Boolean result) {
        //根据预授权结果更新相关信息
        ExecutorService executor = Executors.newFixedThreadPool(1);
        Boolean finalResult = result;
        Runnable task = () -> {
            this.updPreautResult(mchnt_txn_ssn, contract_no, finalResult);
        };

        logger.info("updPreautResult start **********************！");
        executor.execute(task);
        logger.info("updPreautResult end **********************！");
        executor.shutdown();
    }

    private void addIntoLogFile(String out_cust_no, String in_cust_no, String mchnt_txn_ssn, String rem, String resp_code, String resp_desc) {
        //将预授权记录写入日志文件

        ExecutorService executor = Executors.newFixedThreadPool(1);
        Runnable task = new Runnable() {
            public void run() {
                try {
                    String logRecord = DateUtil.transferLongToDate(
                            "yyyy-MM-dd HH:mm:ss", System.currentTimeMillis())
                            + "----富友预授权接口入参:  mchnt_txn_ssn = "
                            + mchnt_txn_ssn
                            + ",out_cust_no = "
                            + out_cust_no
                            + ",in_cust_no = "
                            + in_cust_no
                            + ",rem = " + rem
                            + ",resultCode="
                            + resp_code
                            + ",resp_desc="
                            + resp_desc
                            + "\r\n";
                    logger.info("富友划拨接口入参 = " + logRecord);
                    writeFile(logRecord);

                } catch (Exception e) {
                    logger.error("写绑卡文本文件失败！", e);
                }
            }
        };
        executor.execute(task);
        executor.shutdown();
    }


    @Override
    public Boolean checkToken(String token, Long userId) {
        boolean t = false;
        if (RedisCilent.existsKey(CacheConstants.VALIDATE_TOKEN + token)) {
            String cacheUserId = RedisCilent
                    .getString(CacheConstants.VALIDATE_TOKEN + token);

            Long cacheUserIdLong = Long.valueOf(cacheUserId);
            if (userId.equals(cacheUserIdLong)) {
                t = true;
                RedisCilent.delKey(CacheConstants.VALIDATE_TOKEN + token);
            }
        }

        return t;
    }

    @Override
    public Map<String, String> getRequestMap(Long orderId, Long userId,
                                             String token, String sign, String apiKey) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("userId", userId.toString());
        map.put("orderId", orderId.toString());
        map.put("token", token);
        map.put("sign", sign);
        map.put("apiKey", apiKey);
        map = MapSortUtil.sortMapByKey(map);
        return map;
    }

    @Override
    public String sendPostToFY(String mchnt_txn_ssn, String out_cust_no,
                               String in_cust_no, String amt) {
        String rem = "";
        String pathUrl = pathBaseUrl + "/preAuth.action";
        URIBuilder builder = new URIBuilder().setScheme("https")
                .setHost(jzhBaseUrl).setPath(pathUrl);
        logger.info(builder.toString());
        Map<String, String> trimmedParams = new HashMap<>();

        String allUrl = amt + "|" + in_cust_no + "|" + mchnt_cd + "|"
                + mchnt_txn_ssn + "|" + out_cust_no + "|" + rem;

        SecurityUtils.initPrivateKey();
        SecurityUtils.initPublicKey();
        String signature = SecurityUtils.sign(allUrl);

        trimmedParams.put("mchnt_txn_ssn", mchnt_txn_ssn);
        trimmedParams.put("out_cust_no", out_cust_no);
        trimmedParams.put("in_cust_no", in_cust_no);
        trimmedParams.put("amt", amt);
        trimmedParams.put("rem", rem);
        trimmedParams.put("signature", signature);
        trimmedParams.put("mchnt_cd", mchnt_cd);

        String openResult = FYApiUtil.doPost(builder, trimmedParams);
        return openResult;
    }


    public void updateOrderStatus(Long orderId, Integer payStatus,
                                  Integer confirmStatus, String contract_no) {

        String orderKey = ConstantsOrder.ORDER_FYJZH_PRIX + orderId;
        OrderInfo order = orderHelperService.getFromRedis(orderKey);
        order.setContract_no(contract_no);
        order.setPayStatus(payStatus);
        order.setConfirmStatus(confirmStatus);
       
        orderHelperService.saveOrder(order);

        //添加到订单路由表中
        //orderHelperService.addToRouteInfo(order);
    }

    @Override
    public void addIntoTradeTable(Long orderId, Long userId, String login_id,
                                  BigDecimal amount, String mchnt_txn_ssn, String contract_no,
                                  String resp_code, String resp_desc) {


        ExecutorService executor = Executors.newFixedThreadPool(1);
        Runnable task = new Runnable() {
            public void run() {
                Trade trade = new Trade();
                trade.setUserId(userId);
                trade.setType(ConstantFYJZH.TRADE_TY_YSQ);
                trade.setFlowType(ConstantFYJZH.BALANCE_CUT);
                trade.setMchnt_txn_ssn(mchnt_txn_ssn);
                trade.setLogin_id(login_id);
                trade.setOrderId(orderId);
                trade.setAmt(amount);
                trade.setCts(System.currentTimeMillis());
                trade.setTradeDesc("购买");
                trade.setResp_code(resp_code);
                trade.setContract_no(contract_no);

                tradeService.save(trade);
            }
        };
        executor.execute(task);
        executor.shutdown();



    }

    @Override
    public void writeFile(String record) {
        Long cts = System.currentTimeMillis();
        String pathName = "preauthLogRecords-"
                + DateUtil.transferLongToDate("yyyyMM", cts) + ".txt";

        Path filePath = Paths.get(logFilePath + pathName);

        if (!Files.exists(filePath)) {
            try {
                StringBuilder fileStr = new StringBuilder();
                fileStr.append("0 | | 1\r\n").append(
                        "序号|创建时间|请求流水号|出账账户|入账账户|金额|响应码|\r\n");
                fileStr.append(record);

                Files.write(filePath, fileStr.toString().getBytes("UTF-8"));
            } catch (IOException e) {
                logger.error("首次写文件失败！", e);
            }
        } else {// 存在则追加
            try {
                Files.write(filePath, record.getBytes("UTF-8"),
                        StandardOpenOption.APPEND);
            } catch (IOException e) {
                logger.error("追加写文件失败！", e);
            }

        }

    }

    @Override
    public Boolean  updPreautResult(String mchnt_txn_ssn, String contract_no, Boolean result) {
        Boolean saveOrderResult = false;

    	logger.info("updPreautResult in ********************************");
    	
        Long orderId = null;

        if (RedisCilent.existsKey(mchnt_txn_ssn)) {
            String orderIdStr = RedisCilent.getString(mchnt_txn_ssn);
            orderId = Long.valueOf(orderIdStr);
        }

        logger.info("预授权扣款,更新相关信息 orderId={}, contract_no={}, result={}", orderId, contract_no, result);

        String keyStr = ConstantsOrder.ORDER_FYJZH_PRIX + orderId;

        logger.info("order keyStr={}", keyStr);
        OrderInfo order = orderHelperService.getFromRedis(keyStr);

        if (order == null) {
            logger.error("更新预售权结果，订单在redis中不存在，停止更新。");
            return saveOrderResult;
        }

        if (result) {
            logger.info("预授权扣款成功 更改订单的相关信息 orderId={}", orderId);
            
            int payStatus = ConstantOrderStatus.PAY_STATUS_TRUE;
            
            int confrimStatus = ConstantOrderStatus.CONFIRM_STATUS__NO_CONFIRM;

            order.setPayStatus(payStatus);
            order.setConfirmStatus(confrimStatus);

            order.setContract_no(contract_no);

            //更新redis 中订单相关信息
            logger.info("预授权扣款成功 更新redis 中订单相关信息 orderId={}", orderId);
            orderHelperService.updOrderInRedis(keyStr,order);

            logger.info("预授权扣款成功 保存订单的相关信息 orderId={}", orderId);
            saveOrderResult = orderHelperService.saveOrder(order);
            logger.info("预授权扣款成功 保存订单的结果是 saveOrderResult={}", saveOrderResult);

        
        } else {
            logger.info("预授权扣款失败 更改订单的相关信息 orderId={}", orderId);
            updateOrderStatus(orderId, ConstantOrderStatus.PAY_STATUS_FAIL,
                    ConstantOrderStatus.CONFIRM_STATUS_FALSE, contract_no);
        }

        return saveOrderResult;
    }


    private String getSSN(Long orderId) {

        String ctsLong = String.valueOf(System.currentTimeMillis());

        String cts = ctsLong.substring(ctsLong.length() - 4, ctsLong.length());

        //生成流水号
        String mchnt_txn_ssn = cts + orderId + ConstantFYJZH.MSSN_YSQ;

        return mchnt_txn_ssn;
    }
    
    @Async
    public void succUpdate (Long orderId, Long productId, int payStatus, int confirmStatus, String contract_no, BigDecimal amt, String mchnt_txn_ssn) {
    	try{
    	updateOrderStatus(orderId, ConstantOrderStatus.PAY_STATUS_TRUE,
                ConstantOrderStatus.CONFIRM_STATUS__NO_CONFIRM, contract_no);

        //orderHelperService.updateProductInfo(productId, amt);

        
        RedisCilent.delKey(mchnt_txn_ssn);
        
    	}catch(Exception e){
    		logger.info(" preAuth success update order error : e={}", e);
    	}
    }
    
    
    
    
}